#ifndef __GVCP_H
#define __GVCP_H

#define GVCP_PORT                                 3956
#define GVCP_HEADER_SIZE                          36   /* UDP: 8 Bytes, IP: 20 Bytes, GVCP: 8 Bytes */
#define GVCP_PACKET_SIZE_MAX                      576
#define GVCP_PACKET_DATA_SIZE_MAX                 (GVCP_PACKET_SIZE_MAX - GVCP_HEADER_SIZE)

typedef union {
    /* Command header */
	struct {
	    uint16_t flag;        /* GVCP_COMMAND_FLAG_XXX */
	    uint16_t cmd_type;    /* GvcpCommandType */
	    uint16_t length;      /* Length of payload data */
	    uint16_t req_id;      /* Request ID that was created by the application. */
	                          /* Device copies this value to ack_id field in the ack message */
	                          /* The initial value is 0, application uses req_id by increasing its value (starting from 1) */
	                          /* However when req_id back to 0, it's valid and not necessary to set to 1 */
    } __attribute__((__packed__)) command;
    
    /* Acknowledgement header */
    struct {
	    uint16_t status;      /* GevStatusCode */
	    uint16_t ack_type;    /* GvcpCommandType */
	    uint16_t length;      /* Length of payload data */
	    uint16_t ack_id;      /* With the same value as req_id */
    }  __attribute__((__packed__)) acknowledge;
    
} GvcpHeader;

#define GVCP_COMMAND_CODE                         0x4200
#define GVCP_COMMAND_FLAG_CLEAR                   0x4200
#define GVCP_COMMAND_FLAG_MUST_ACK                0x4201
#define GVCP_COMMAND_FLAG_BROADCAST_ACK           0x4210


#define GVCP_CHECK_PAYLOAD_LENGTH (x)             !(x & 0x1f)

typedef struct {
	GvcpHeader    header;
	unsigned char payload[];
} GvcpPacket;

typedef struct {
    uint16_t spec_version_major;
    uint16_t spec_version_minor;
    uint32_t device_mode;
    uint16_t reserved00;
    uint16_t device_mac_high;
    uint16_t device_mac_low;
    uint32_t ip_config_options;
    uint32_t ip_config_current;
    uint32_t reserved01;
    uint32_t reserved02;
    uint32_t reserved03;
    uint32_t current_ip;
    uint32_t reserved04;
    uint32_t reserved05;
    uint32_t reserved06;
    uint32_t current_subnet_mask;
    uint32_t reserved07;
    uint32_t reserved08;
    uint32_t reserved09;
    uint32_t default_gateway;
    uint8_t  manufacturer_name[32];
    uint8_t  model_name[32];
    uint8_t  device_version[32];
    uint8_t  manufacturer_info[48];
    uint8_t  serial_number[16];
    uint8_t  user_defined_name[16];
} __attribute__((__packed__)) GvcpDataDiscoveryAck;

typedef struct {
    uint16_t reserved00;
    uint16_t mac_high;
    uint16_t mac_low;
    uint32_t reserved01;
    uint32_t reserved02;
    uint32_t reserved03;
    uint32_t ip_addr;
    uint32_t reserved04;
    uint32_t reserved05;
    uint32_t reserved06;
    uint32_t subnet_mask;
    uint32_t reserved07;
    uint32_t reserved06;
    uint32_t reserved09;
    uint32_t default_gateway;
} __attribute__((__packed__)) GvcpDataForceIpCmd;

typedef struct {
    uint16_t reserved;
    uint16_t index;
} __attribute__((__packed__)) GvcpDataWriteRegAck, GvcpDataWriteMemAck;

typedef struct {
    uint32_t address;
    uint16_t reserved;
    uint16_t count;
} __attribute__((__packed__)) GvcpDataReadMemCmd;

typedef struct {
    uint32_t address;
    uint8_t  data[];
} __attribute__((__packed__)) GvcpDataReadMemAck, GvcpDataWriteMemCmd;

typedef struct {
    uint16_t stream_channel_index;
    uint16_t block_id;
    uint32_t first_packet_id;
    uint32_t last_packet_id;
} __attribute__((__packed__)) GvcpDataPktResendCmd;

typedef struct {
    uint16_t reserved;
    uint16_t time_to_completion;  /* in millisecond */
} __attribute__((__packed__)) GvcpDataPendingAck;

typdef struct {
    uint32_t device_key;
    uint32_t group_key;
    uint32_t group_mask;
} __attribute__((__packed__)) GvcpDataActionCmd;

typedef enum {
    /* Discovery Protocol */
	GVCP_COMMAND_DISCOVERY_CMD      = 0x0002,
	GVCP_COMMAND_DISCOVERY_ACK      = 0x0003,
	GVCP_COMMAND_FORCEIP_CMD        = 0x0004,
	GVCP_COMMAND_FORCEIP_ACK        = 0x0005,
	/* Streaming Protocol */
	GVCP_COMMAND_PACKET_RESEND_CMD  = 0x0040,
	GVCP_COMMAND_PACKET_RESEND_ACK  = 0x0041,
	/* Device Memory Access */
	GVCP_COMMAND_READREG_CMD        = 0x0080,
	GVCP_COMMAND_READREG_ACK        = 0x0081,
	GVCP_COMMAND_WRITEREG_CMD       = 0x0082,
	GVCP_COMMAND_WRITEREG_ACK       = 0x0083,
	GVCP_COMMAND_READMEM_CMD        = 0x0084,
	GVCP_COMMAND_READMEM_ACK        = 0x0085,
	GVCP_COMMAND_WRITEMEM_CMD       = 0x0086,
	GVCP_COMMAND_WRITEMEM_ACK       = 0x0087,
	GVCP_COMMAND_PENDING_ACK        = 0x0089,
	/* Asyncronous Events */
	GVCP_COMMAND_EVENT_CMD          = 0x00c0,
	GVCP_COMMAND_EVENT_ACK          = 0x00c1,
	GVCP_COMMAND_EVENTDATA_CMD      = 0x00c2,
	GVCP_COMMAND_EVENTDATA_ACK      = 0x00c3,
	/* Miscellaneous */
	GVCP_COMMAND_ACTION_CMD         = 0x0100,
	GVCP_COMMAND_ACTION_ACK         = 0x0101
} GvcpCommandType;

typedef enum {
    GEV_STATUS_SUCCESS                             = 0x0000,
    GEV_STATUS_PACKET_RESEND                       = 0x0100,
    GEV_STATUS_NOT_IMPLEMENTED                     = 0x8001,
    GEV_STATUS_INVALID_PARAMETER                   = 0x8002,
    GEV_STATUS_INVALID_ADDRESS                     = 0x8003,
    GEV_STATUS_WRITE_PROTECT                       = 0x8004,
    GEV_STATUS_BAD_ALIGNMENT                       = 0x8005,
    GEV_STATUS_ACCESS_DENIED                       = 0x8006,
    GEV_STATUS_BUSY                                = 0x8007,
    GEV_STATUS_LOCAL_PROBLEM                       = 0x8008,
    GEV_STATUS_MSG_MISMATCH                        = 0x8009,
    GEV_STATUS_INVALID_PROTOCOL                    = 0x800a,
    GEV_STATUS_NO_MSG                              = 0x800b,
    GEV_STATUS_PACKET_UNAVAILABLE                  = 0x800c,
    GEV_STATUS_DATA_OVERRUN                        = 0x800d,
    GEV_STATUS_INVALID_HEADER                      = 0x800e,
    GEV_STATUS_WRONG_CONFIG                        = 0x800f,
    GEV_STATUS_PACKET_NOT_YET_AVAILABLE            = 0x8010,
    GEV_STATUS_PACKET_AND_PREV_REMOVED_FROM_MEMORY = 0x8011,
    GEV_STATUS_PACKET_REMOVED_FROM_MEMORY          = 0x8012,
    GEV_STATUS_ERROR                               = 0x8fff
} GevStatusCode;

/* Bootstrap register */
#define GVBS_VERSION_OFFSET	                       0x00000000
# define GVBS_VERSION_MINOR_MASK                    0x0000ffff
# define GVBS_VERSION_MINOR_POS                     0
# define GVBS_VERSION_MAJOR_MASK                    0xffff0000
# define GVBS_VERSION_MAJOR_POS                     16

#define GVBS_DEVICE_MODE_OFFSET	                   0x00000004
# define GVBS_DEVICE_MODE_CHARACTER_SET_MASK        0x000000ff
# define GVBS_DEVICE_MODE_CHARACTER_SET_POS         0
# define GVBS_DEVICE_MODE_ENDIAN_POS                31

#define GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET        0x00000008
#define GVBS_DEVICE_MAC_ADDRESS_LOW_OFFSET         0x0000000c

#define GVBS_SUPPORTED_IP_CONFIGURATION_OFFSET     0x00000010
#define GVBS_CURRENT_IP_CONFIGURATION_OFFSET       0x00000014
# define GVBS_IP_CONFIGURATION_PERSISTENT           1
# define GVBS_IP_CONFIGURATION_DHCP                 2
# define GVBS_IP_CONFIGURATION_LLA                  4

#define GVBS_CURRENT_IP_ADDRESS_OFFSET             0x00000024
#define GVBS_CURRENT_SUBNET_MASK_OFFSET            0x00000034
#define GVBS_CURRENT_GATEWAY_OFFSET                0x00000044

#define GVBS_MANUFACTURER_NAME_OFFSET              0x00000048
# define GVBS_MANUFACTURER_NAME_SIZE                32
#define GVBS_MODEL_NAME_OFFSET                     0x00000068
# define GVBS_MODEL_NAME_SIZE                       32
#define GVBS_DEVICE_VERSION_OFFSET                 0x00000088
# define GVBS_DEVICE_VERSION_SIZE                   32
#define GVBS_MANUFACTURER_INFORMATIONS_OFFSET      0x000000a8
# defiine GVBS_MANUFACTURER_INFORMATIONS_SIZE       48
#define GVBS_SERIAL_NUMBER_OFFSET                  0x000000d8 // [Optional]
# define GVBS_SERIAL_NUMBER_SIZE                    16
#define GVBS_USER_DEFINED_NAME_OFFSET              0x000000e8 // [Optional]
# define GVBS_USER_DEFINED_NAME_OFFSET              16

#define GVBS_XML_URL_0_OFFSET                      0x00000200
#define GVBS_XML_URL_1_OFFSET                      0x00000400
# define GVBS_XML_URL_SIZE                          512

#define GVBS_NUMBER_OF_NETWORK_INTERFACES_OFFSET   0x00000600

#define	GVBS_PERSISTENT_IP_ADDRESS_0_OFFSET		   0x0000064c // [Optional]
#define	GVBS_PERSISTENT_SUBNET_MASK_0_OFFSET	   0x0000065c // [Optional]
#define	GVBS_PERSISTENT_GATEWAY_0_OFFSET		   0x0000066c // [Optional]

#define GVBS_LINK_SPEED_0_OFFSET                   0x00000670 // [Optional]

#define GVBS_NUMBER_OF_MESSAGE_CHANNELS_OFFSET     0x00000900 // 0~1
#define GVBS_NUMBER_OF_STREAM_CHANNELS_OFFSET      0x00000904 // 1~512
#define GVBS_NUMBER_OF_ACTION_SIGNALS_OFFSET       0x00000908 // [Optional] 0~128

#define GVBS_ACTION_DEVICE_KEY_OFFSET              0x0000090c // [Optional]

#define GVBS_STREAM_CHANNELS_CAPABILITY_OFFSET     0x0000092c // [Optional]
#define GVBS_MESSAGE_CHANNEL_CAPABILITY_OFFSET     0x00000930 // [Optional]
# define GVBS_CAPABILITY_SUPPORT_SCSP               (1 << 31)

#define GVBS_GVCP_CAPABILITY_OFFSET                0x00000934
// This register reports the bootstrap device is supported by the control register and channel GVCP arbitrary commands. 
// Some of these properties that are supported when setting register GVCP (address 0x0954) is enabled through.
# define GVBS_GVCP_CAPABILITY_CONCATENATION         (1 << 0) // Support multiple operation in a single message
# define GVBS_GVCP_CAPABILITY_WRITE_MEMORY          (1 << 1) // Support WRITEMEM_CMD & WRITEMEM_ACK
# define GVBS_GVCP_CAPABILITY_PACKET_RESEND         (1 << 2) // Support PACKETRESEND_CMD
# define GVBS_GVCP_CAPABILITY_EVENT                 (1 << 3) // Support EVENT_CMD & EVENT_ACK
# define GVBS_GVCP_CAPABILITY_EVENT_DATA            (1 << 4) // Support EVENTDATA_CMD & EVENTDATA_ACK
# define GVBS_GVCP_CAPABILITY_PENDING               (1 << 5) // Support PENDING_ACK
# define GVBS_GVCP_CAPABILITY_ACTION                (1 << 6) // Support ACTION_CMD & ACTION_ACK
# define GVBS_GVCP_CAPABILITY_EXTENSTION_STATUS     (1 << 22) // GEV_STATUS_PACKET_RESEND, GEV_STATUS_WRONG_CONFIG, GEV_STATUS_PACKET_NOT_YET_AVAILABLE, GEV_STATUS_PACKET_AND_PREV_REMOVED_FROM_MEMORY, GEV_STATUS_PACKET_REMOVED_FROM_MEMORY
# define GVBS_GVCP_CAPABILITY_CAN_WRITE_DISCOVERY_ACK_DELAY (1 << 23) // Application can write discovery ack delay register if this is set
# define GVBS_GVCP_CAPABILITY_DISCOVERY_ACK_DELAY   (1 << 24) // Support discovery ack delay register
# define GVBS_GVCP_CAPABILITY_TEST_DATA             (1 << 25) // Test packet is filled with data from the generator LFSR.
# define GVBS_GVCP_CAPABILITY_MANIFEST_TABLE        (1 << 26) // Manifest tables are supported. When an application must be supported and taken an XML device description file with the manifest table.
# define GVBS_GVCP_CAPABILITY_CCP_APPLICATION_PORT_AND_IP  (1 << 27) // Manifest tables are supported. When an application must be supported and taken an XML device description file with the manifest table.
# define GVBS_GVCP_CAPABILITY_LINK_SPEED            (1 << 28) // Support link speed register
# define GVBS_GVCP_CAPABILITY_HEARTBEAT_DISABLED    (1 << 29) // The heartbeat can be disabled
# define GVBS_GVCP_CAPABILITY_SERIAL_NUMBER         (1 << 30) // Support serial number register
# define GVBS_GVCP_CAPABILITY_USER_DEFINED_NAME     (1 << 31) // Support user-defined name

#define GVBS_HEARTBEAT_TIMEOUT_OFFSET              0x00000938 // Heartbeat timeout in millisecond
#define GVBS_TIMESTAMP_TICK_FREQUENCY_HIGH_OFFSET  0x0000093c // [Optional]
#define GVBS_TIMESTAMP_TICK_FREQUENCY_LOW_OFFSET   0x00000940 // [Optional]
#define GVBS_TIMESTAMP_CONTROL_OFFSET              0x00000944 // [Optional]
# define GVBS_TIMESTAMP_CONTROL_RESET               (1 << 0)  // Reset timestamp latched value (0x948/0x94c)
# define GVBS_TIMESTAMP_CONTROL_LATCH               (1 << 1)  // Latch current timestamp in 0x948/0x94c
#define GVBS_TIMESTAMP_LATCHED_VALUE_HIGH_OFFSET   0x00000948 // [Optional]
#define GVBS_TIMESTAMP_LATCHED_VALUE_LOW_OFFSET    0x0000094c // [Optional]

#define GVBS_DISCOVERY_ACK_DELAY_OFFSET            0x00000950 // [Optional]
// [CR27-41d] If you register late discovery writable ACK, its value must be stored in a persistent non-volatile memory has to be reused when the next reset or power into.
// [CR27-42d] If you register late discovery is supported ACK, the factory default setting of this register up to1000ms (1 second) must be less than or equal to.
#define GVBS_GVCP_CONFIGURATION_OFFSET             0x00000954 // [Optional]
// This optional register to provide additional controls GVCP.
// Additional capability features of these registers GVCP (address 0x0934) must be shown to.
// For example, when this performance is supported, you can use when you disable the heartbeat. Also useful for debugging.
#define GVBS_PENDING_TIMEOUT_OFFSET                0x00000958 // [Optional]

#define GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET      0x00000a00 // CCP
// This register is used to grant privileges to the application.
// It is possible to control a device that only one application.
// This application can write to the register of the device. 
// Other applications can read the registers of the device only when the application does not have the privilege of control rights.
# define GVBS_CONTROL_CHANNEL_PRIVILEGE_EXCLUSIVE   (1 << 0) // Only 1 can control the device, but others can monitor
# define GVBS_CONTROL_CHANNEL_PRIVILEGE_CONTROL     (1 << 1) // Only 1 can control and monitor the device

#define GVBS_PRIMARY_APPLICATION_PORT_OFFSET       0x00000a04 // [Optional]
// UDP source port of the main application. 
// This value must be zero if no key applications on the device. (CCP register is equal to 0)
#define GVBS_PRIMARY_APPLICATION_IP_ADDRESS_OFFSET 0x00000a14 // [Optional]
// IPv4 address key applications. MUST be a unicast address. 
// This value must be zero if no key applications on the device. (CCP register is equal to 0)

#define GVBS_MCP_OFFSET                            0x00000b00 // Message Control Port [Optional]
// When supported, the application shall activate the channel by writing messages to host_port nonzero field. Otherwise, the closed channel.
# define GVBS_MCP_HOST_PORT_MASK                    0x0000ffff // A message must be sent to the device from the port. A message channel is set to close this value to 0.
# define GVBS_MCP_NETWORK_INTERFACE_INDEX_MASK      0x000f0000 // Alway 0

#define GVBS_MCDA_OFFSET                           0x00000b10 // Message Channel Destination Address [Optional]
#define GVBS_MCTT_OFFSET                           0x00000b14 // Message Channel Transmition Time (ms) [Optional]
#define GVBS_MCRC_OFFSET                           0x00000b18 // Message Channel Retry Count [Optional]
#define GVBS_MCSP_OFFSET                           0x00000b1c // Message Channel Source Port [Optional]


//
// Stream Channel Registers
//
#define GVBS_SCP_OFFSET                            0x00000d00 // Stream Channel Port
# define GVBS_SCP_HOST_PORT_MASK                    0x0000ffff // Port that the device must send a data stream. The stream channel and close to zero value.
# define GVBS_SCP_NETWORK_INTERFACE_INDEX_MASK      0x000f0000 // The index of the network interface (3-0). A particular stream is hard coded to a specific network interface. The devices in this field may not be the program. This is a read-only.

#define GVBS_SCPS_OFFSET                           0x00000d04 // Stream Channel Packet Size
# define GVBS_SCPS_PACKET_SIZE_MASK                 0x0000ffff
# define GVBS_SCPS_PACKET_BIG_ENDIAN                (1 << 29)
# define GVBS_SCPS_DO_NOT_FRAGMENT                  (1 << 30)
# define GVBS_SCPS_EMIT_TEST_PACKET                 (1 << 31)
// When this bit is set, the device emits a single test packet size that is specified in bits 16-31. 
// TheIP header "don't fragment" bit must be set for this test packet. 
// If the device supports the LFSRgenerator load test packet to be filled with data by a polynomial. 
// Otherwise, load data "don't care" becomes.

#define GVBS_SCPD_OFFSET                           0x00000d08 // Stream Channel Packet Delay
// This delay is the same precision instruments utilize the time stamp in order to ensure the accuracy of the normal packet delays. 
// This instrument is used very frequently. Internal packet delay is very small. 
// If the timestamp is not supported, this register is not effective.

#define GVBS_SCDA_OFFSET                           0x00000d18 // Stream Channel Destination Address
// IPv4 destination address stream channel. The destination address is multicast or unicast address.

#define GVBS_SCSP_OFFSET                           0x00000d1c // Stream Channel Source Port [Optional]
// This optional register indicating the source UDP port for a particular stream channel
// The purpose of this information is to take measures to ensure that the device back to host streaming traffic from the host application.
// Expected usage of the device sends a source port from UDP packet to port the application receives a stream of false, which seem to be required by the application rather than a stream of unsolicited traffic by doing so.
//
// [CR27-45d] If supported by the device, SCPSx must return a nonzero value for the source UDP port of the stream channel is not zero when SCSPx. 
//            During the control session, as long as the non-zero constant is valid.
// [CR27-46d] If supported by the device, if not zero SCDAx SCSPx not focus on the device and SCPx SCSPx port (if it is open if the stream channel) and ignore the UDP traffic coming from the address and port combination that is listed in must be.

#define GVBS_SCC_OFFSET                            0x00000d20 // Stream Channel Capability [Optional]
// Any of these registers gives a list of specific capability in a particular stream channel.
# define GVBS_SCC_SUPPORT_BIG_ENDIAN                1
// This indicates that the stream channel to support big and little endian. 
// Stream channel to keep in mind that all must support the little-endian.

// For Stream Channel Registers (n = 0 ~ 511)
#define GVBS_SCx_OFFSET(reg, n)                    (reg + 0x40 * n)
// For example:
//    SCP1  -> GVBS_SCx_OFFSET(GVBS_SCP_OFFSET, 1)
//    SCSP2 -> GVBS_SCx_OFFSET(GVBS_SCSP_OFFSET, 2)

#define GVBS_MANIFEST_TABLE_OFFSET                 0x00009000 // [Optional]
# define GVBS_MANIFEST_TABLE_ENTRY_SIZE             8
# define GVBS_MANIFEST_TABLE_ENTRY_MAX              64

#define GVBS_ACTION_GROUP_KEY_0_OFFSET             0x00009800 // [Optional]
#define GVBS_ACTION_GROUP_MASK_0_OFFSET            0x00009804 // [Optional]

#define GVBS_MANUFACTURER_SPECIFIC_SPACE_OFFSET    0x0000a000

#endif
